# module name lexer.py
# $Id: lexer.py ddcad87fab9c 2013/08/17 19:20:30 pgurumur $
# Copyright (c) 2013 Prabhu Gurumurthy <pgurumur@gmail.com>
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
# File lexer.py generated on Thu, August 08, 2013 13:02:33 PM
#
__version__  = "$Revision: ddcad87fab9c $"
__author__   = "$Author: pgurumur $"
__modified__ = "$Date: 2013-08-17 19:20:30Z $"

import re

from pyparsing import alphas, alphanums, delimitedList
from pyparsing import nums, oneOf, pythonStyleComment, quotedString
from pyparsing import removeQuotes, restOfLine, operatorPrecedence, opAssoc
from pyparsing import Combine, Group, Keyword, Literal, LineEnd, OneOrMore, Or
from pyparsing import ParseFatalException, ParserElement, StringEnd, Forward
from pyparsing import Suppress, Word, ZeroOrMore, ParseException, Optional
from pyparsing import Empty, CaselessLiteral, lineno, printables

try:
   from . import exception, stdlib, base
except ImportError as err:
   import exception, stdlib, base

def set_line(function):
   def wrapper(*args, **kwargs):
      retval = function(*args, **kwargs)
      retval.line = lineno(args[2], args[1])
      return retval
   return wrapper

class Namespace(base.BerylNamespace):
   def __init__(self, Name = None):
      super(Namespace, self).__init__(Name)
      self._lb, self._rb = map(Suppress, "{}")
      self._lp, self._rp = map(Suppress, "()")
      self._lbk, self._rbk = map(Suppress, "[]")
      self._lt, self._gt = map(Suppress, "<>")
      self._assign = oneOf("= += * *= -= /= |= &= ^=")
      self._plusop = Literal("+") | Literal("-")
      self._multop = Literal("*") | Literal("/")
      self._factop = Literal("!")
      self._expop = Literal("^")
      self._compop = oneOf("< <= > >= == != <>")
      self._signop = oneOf("+ -")
      self._logcop = oneOf("& ^ |")
      self._comma = Literal(",").suppress()
      self._fs = Literal("/").suppress()

      self._identifier = Word(alphas + alphanums + "-_").setParseAction(
            self.validateIdentifier)
      self._quoted = quotedString.copy().setParseAction(
            removeQuotes).setParseAction(self.setString)
      self._integer = Combine(Optional(oneOf("- +")) + Word(
         nums)).setParseAction(self.setInteger)
      self._float = Combine(Optional(oneOf("- +")) + Word(nums) + "." + Word(
         nums)).setParseAction(self.setFloat)
      self._boolean = oneOf("true false").setParseAction(self.setBoolean)
      self._fparams = self._lp + Optional(self._identifier + Optional(
         ZeroOrMore(self._comma + self._identifier))) + self._rp
      self._fcall = Group(self._identifier + self._fparams)
      self._regex = self._lbk + Word(printables) + self._rbk

      self._rule = Forward()

   def kw(self, string):
      retval = None
      if string:
         base.kw_add(string)
         retval = Keyword(string)

      return retval

   @set_line
   def setString(self, string, location, tokens):
      bstring = tokens[0]
      bstring = bstring.replace("\"", "")
      return base.BerylVariable(str(bstring))

   @set_line
   def setFloat(self, string, location, tokens):
      return base.BerylVariable(float(tokens[0]))

   @set_line
   def setInteger(self, string, location, tokens):
      return base.BerylVariable(int(tokens[0]))

   @set_line
   def setBoolean(self, string, location, tokens):
      retval = None
      if tokens[0] == 'true':
         retval = base.BerylTrue()
      elif tokens[0] == 'false':
         retval = base.BerylFalse()

      return retval

   def namespace(self, string, location, tokens):
      print(tokens)

   def simpleAssignment(self, string, location, tokens):
      print(tokens)

   def validateIdentifier(self, string, location, tokens):
      repat = re.compile(r'\d+([a-zA-Z-_]+)')
      match = repat.search(tokens[0])
      if match:
         print("invalid variable %s in line %d" %(tokens[0], lineno(
            location, string)))

   def validateIndex(self, string, location, tokens):
      print(tokens)

   def setFunction(self, string, location, tokens):
      print(tokens)

   def tagstart(self, string, location, tokens):
      print("start", tokens)

   def tagstop(self, string, location, tokens):
      print("stop", tokens)

   def printStatement(self, string, location, tokens):
      print(tokens)

   @property
   def grammar(self):
      colon = Literal(":").suppress()
      comma = Literal(",").suppress()
      numbers = Word(nums)
      argList = (self._float | self._integer | self._boolean | self._fcall |
            self._identifier | self._quoted)

      simpleStmt = (self._identifier.setParseAction(self.validateIdentifier) +
            self._assign + argList).setParseAction(self.simpleAssignment)
      index = self._lbk + (
            (self._fcall + colon + self._fcall) |
            (self._fcall + colon + numbers) |
            (self._fcall + colon) |
            (numbers + colon + numbers) |
            (numbers + colon + self._fcall) |
            (numbers + colon) |
            (colon + numbers) |
            (colon + self._fcall) | numbers) + self._rbk
      indexStmt = (
            self._identifier + self._assign + (
               self._identifier + index).setParseAction(self.validateIndex)
      ).setParseAction(self.simpleAssignment)

      varargs = self._quoted + comma + OneOrMore(self._float |
            self._integer | self._boolean | self._fcall | self._identifier)
      varList = argList + Optional(comma + ZeroOrMore(argList))
      printStmt = (Keyword("print").suppress() + self._lp + (varargs |
         varList) + self._rp).setParseAction(self.printStatement)

      regexStmt = (self._identifier.setParseAction(self.validateIdentifier) +
            Literal("=~") + self._regex)

      statement = (indexStmt | simpleStmt | printStmt)

      retvalue = self.kw("return") + Optional(self._identifier |
            self._fparams)
      fstatement = (self.kw("function") + self._identifier).setParseAction(
            self.tagstart) + self._fparams + ZeroOrMore(delimitedList(
               statement | self._rule)) + Optional(
                     retvalue) + self.kw("end").setParseAction(self.tagstop)
      """
      fstatement = (self.kw("function").setParseAction(
         self.tagstart) + self._fcall + ZeroOrMore(delimitedList(
               statement | self._rule)) + Optional(retvalue) +
            self.kw("end").setParseAction(self.tagstop)).setParseAction(
                  self.setFunction)
      """

      self._rule = ZeroOrMore(delimitedList(
         fstatement | statement )) + StringEnd()
      self._rule.ignore(Literal("\\") + LineEnd())
      self._rule.ignore(pythonStyleComment)
      return self._rule

   def __call__(self, *args, **kwargs):
      results = None
      if len(args) == 1:
         print("tokens")
      else:
         stdin = False
         filename = None
         fname = None
         if "stdin" in kwargs:
            stdin = kwargs["stdin"]

         if "filename" in kwargs:
            filename = kwargs["filename"]

         if stdin:
            fname = "stdin"
         else:
            fname = filename

         try:
            if stdin:
               results = self.grammar.parseString(filename.read())
            else:
               with open(filename, "r") as fh:
                  results = self.grammar.parseString(fh.read())
         except ParseFatalException as err:
            self.error("parsing failed on %s: %s", fname, err)
         except ParseException as err:
            self.error("syntax error on line %d col %d in file %s",
                  err.lineno, err.col, fname)
         except exception.InternalError as err:
            self.error("%s in file %s", str(err), fname)
         except exception.CompileError as err:
            self.error("%s in file %s", str(err), fname)
         except exception.LexerError as err:
            self.error("%s in file %s", str(err), fname)
